import 'dart:async';

import 'package:eplorex/im/message_frame.dart';
import 'package:eplorex/im/streamtool_utils.dart';
import 'package:eplorex/infra/websocket_event_handler.dart';
import 'package:eplorex/utils/Api.dart';
import 'package:flutter/cupertino.dart';
import 'package:rxdart/rxdart.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/status.dart' as status;
import 'package:web_socket_channel/web_socket_channel.dart';

import '../constants/network_constants.dart';

typedef WebsocketMessageCallBack = void Function(dynamic message);

enum StreamControllerNameEnum {
  customerLoginPage,
  customerMainPage,
  chatRoomPage,
}

enum NetStatus {
  connecting,
  connected,
  reconnected,
  disconnected;
}

class WebsocketUtils {
  WebsocketUtils._();

  static WebsocketUtils? _singleton;

  factory WebsocketUtils() => _singleton ??= WebsocketUtils._();

  Uri? wsUri;

  WebSocketChannel? _webSocketChannel;

  Map<String, BehaviorSubject<String>>? streamControllers;

  bool isDisconnectByUser = false;

  bool isOpenHeartBeat = true;

  StreamSubscription<String>? _subscription;

  WebsocketEventHandler? eventHandler;

  NetStatus? _netStatus;

  static ValueNotifier<NetStatus> netStatusNotifier =
      ValueNotifier<NetStatus>(NetStatus.disconnected);

  static initNetWork() {
    WebsocketUtils()?._initSocket(wsPath: Api.BASE_WSS_URL);
  }

  _initSocket({required String wsPath, bool isOpenHeartBeat = true}) {
    if (_netStatus == NetStatus.connected ||
        _netStatus == NetStatus.reconnected) {
      return;
    }
    if (_webSocketChannel != null) {
      return;
    }
    wsUri = Uri.tryParse("$wsPath");
    if (wsUri == null) return;
    this.isOpenHeartBeat = isOpenHeartBeat;
    _connectWebsocket(wsPath: wsPath);
  }

  void _connectWebsocket(
      {String? wsPath,
      bool isRunForReconnect = false,
      bool isReconnect = false}) async {
    _webSocketChannel = WebSocketChannel.connect(wsUri ?? Uri.parse(wsPath!));
    _netStatus = NetStatus.connecting;
    netStatusNotifier.value = NetStatus.connecting;
    if (!isRunForReconnect) {
      isDisconnectByUser = false;
    }
    // 连接状态变更
    _webSocketChannel?.ready.then((value) {
      isDisconnectByUser = false;
      _netStatus = isReconnect ? NetStatus.reconnected : NetStatus.connected;
      netStatusNotifier.value = _netStatus!;
      if (isOpenHeartBeat) {
        if (_webSocketChannel is IOWebSocketChannel) {
          (_webSocketChannel as IOWebSocketChannel)
              .innerWebSocket
              ?.pingInterval = const Duration(seconds: 5);
        }

        // _heartBeat();
      }
    });
  }

  static void listen(WebsocketEventHandler eventHandler) {
    _singleton?.eventHandler ??= eventHandler;
    WebsocketUtils()._listen(eventHandler);
  }

  void _listen(WebsocketEventHandler eventHandler) {
    streamControllers ??= <String, BehaviorSubject<String>>{};
    _webSocketChannel?.stream.listen((message) {
      if (message is String && message.isEmpty) {
        return;
      }
      streamControllers?.forEach((key, value) {
        if (!value.isClosed) {
          value.sink.add(message);
        }
      });
      this.eventHandler?.onMessage(message);
    }, onDone: () {
      this.eventHandler?.onDone();
      _netStatus = NetStatus.disconnected;
      netStatusNotifier.value = NetStatus.disconnected;
      _reConnect();
    }, onError: (Object error, StackTrace stackTrace) {
      this.eventHandler?.onError(error);
    }, cancelOnError: false);
  }

  void _heartBeat() {
    if (_subscription != null) {
      return;
    }

    Future.delayed(const Duration(seconds: 3), () {
      var pollingStream = StreamTool().timedPolling(
          const Duration(seconds: 5), () => Future(() => ""), 100000000);
      _subscription = pollingStream.listen((result) {
        _webSocketChannel?.sink.add(_pingFrame());
      });
    });
  }

  void _reConnect() {
    if (isDisconnectByUser) {
      return;
    }
    Future.delayed(
      new Duration(seconds: 3),
      () {
        try {
          _subscription?.cancel();
          _subscription = null;
          _webSocketChannel?.sink.close(status.abnormalClosure, "断线重链接");
          _webSocketChannel = null;
          _connectWebsocket(isReconnect: true);
          _listen(eventHandler!);
        } catch (e) {
          _webSocketChannel = null;
        }
      },
    );
  }

  int sendMessage({required String message, bool needDisplayMsg = true}) {
    if (needDisplayMsg) {
      streamControllers?.forEach((key, value) {
        if (!value.isClosed) {
          value.sink.add(message);
        }
      });
    }
    _webSocketChannel?.sink.add(message);
    return 0;
  }

  void disConnect({bool isDisconnectByUser = false}) {
    this.isDisconnectByUser = isDisconnectByUser;
    _subscription?.cancel();
    _subscription = null;
    streamControllers?.forEach((key, value) {
      value.close();
    });
    streamControllers?.clear();
    _webSocketChannel?.sink.close(status.normalClosure, "user close");
    _webSocketChannel = null;
  }

  setNewStreamController(StreamControllerNameEnum streamControllerName) {
    if (streamControllers?.containsKey(streamControllerName.name) ?? false) {
      streamControllers?[streamControllerName.name]?.close();
    }
    streamControllers?[streamControllerName.name] = BehaviorSubject();
  }

  MessageFrame _pingFrame() {
    return MessageFrame(header: Header(type: 0));
  }
}
